﻿// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

#define PATCH_FINISH    0xFFFFFFFF
#define PATCH_JMP       0x000000E9
#define PATCH_CALL      0x000000E8
#define PATCH_RETN      0x000000C3
#define PATCH_RETN4     0x000004C2
#define PATCH_RETN8     0x000008C2
#define PATCH_RETN0C    0x00000CC2
#define PATCH_RETN10    0x000010C2
#define PATCH_RETN14    0x000014C2
#define PATCH_RETN18    0x000018C2
#define PATCH_RETN1C    0x00001CC2
#define PATCH_NOPBLOCK  0x90909090

typedef struct 
{
    DWORD dwAddress;
    DWORD dwData;
    BOOL boolRelative;
    size_t iPatchSize;
} DLLPatchStrc;

void D2SE_CompareGameVersion();
void D2SE_IsCoreSupport();
void D2SE_IsCoreInstall();
void D2SE_HookD2Dll();
void D2SE_EnableTxt();
void D2SE_PatchDllPath();

static const DLLPatchStrc m_astPatches[] =
{
    {0xE064, 0x0D, FALSE, 0x01},    //比较的版本数量从8变成13
    {0xE04D, PATCH_JMP, FALSE, 0x01},   //跳转到新的判断代码
    {0xE04E, (DWORD)D2SE_CompareGameVersion, TRUE, 0x00},
    {0xE052, PATCH_NOPBLOCK, FALSE, 0x02},

    {0x10301, PATCH_JMP, FALSE, 0x01},   //检查对应版本的core是否support
    {0x10302, (DWORD)D2SE_IsCoreSupport, TRUE, 0x00},
    {0x10306, PATCH_NOPBLOCK, FALSE, 0x02},

    {0x10AB7, PATCH_JMP, FALSE, 0x01},   //检查对应版本的core是否已安装 
    {0x10AB8, (DWORD)D2SE_IsCoreInstall, TRUE, 0x00},
    {0x10ABC, PATCH_NOPBLOCK, FALSE, 0x01},

    {0x1A57, PATCH_JMP, FALSE, 0x01},   //hook一堆D2的dll函数
    {0x1A58, (DWORD)D2SE_HookD2Dll, TRUE, 0x00},

    {0xD455, PATCH_JMP, FALSE, 0x01},   //使能1.13d版本的-txt选项
    {0xD456, (DWORD)D2SE_EnableTxt, TRUE, 0x00},
    {0xD45A, PATCH_NOPBLOCK, FALSE, 0x01},

    {0x114CB, PATCH_JMP, FALSE, 0x01},    //修改dll的定位目录
    {0x114CC, (DWORD)D2SE_PatchDllPath, TRUE, 0x00},
    {0x114D0, PATCH_NOPBLOCK, FALSE, 0x02},

    {PATCH_FINISH} // this must be the last entry in the array!
};

const char *m_apcExtendVersion[] = {"1.13d", "1.14a", "1.14b", "1.14c", "1.14d"};
const DWORD m_dwPatchDllRetAddr1 = 0x004114D2;
const DWORD m_dwPatchDllRetAddr2 = 0x004114D5;

__declspec(naked) void D2SE_PatchDllPath()
{
    __asm
    {
        cmp edx, 0x7
        jg d113
        mov edx,dword ptr ds:[edx*4+0x417988]
        jmp m_dwPatchDllRetAddr1
     d113:
        mov edx,m_apcExtendVersion[0]
        jmp m_dwPatchDllRetAddr2
    }
}

const DWORD m_dwCompareRetAddr1 = 0x0040E054;
const DWORD m_dwCompareRetAddr2 = 0x0040E05E;
const DWORD m_dwCompareCallAddr = 0x0040ACF0;

__declspec(naked) void D2SE_CompareGameVersion()
{
    __asm
    {
        add esp,0x8
        test eax,eax
        je out1
        push m_apcExtendVersion[0]
        push 0x00419FE4
        call m_dwCompareCallAddr
        add esp,0x8
        test eax,eax
        jnz a114
        mov dword ptr ds:[esi+0x210],0x8
        jmp m_dwCompareRetAddr2
    a114:
        push m_apcExtendVersion[1]
        push 0x00419FE4
        call m_dwCompareCallAddr
        add esp,0x8
        test eax,eax
        jnz b114
        mov dword ptr ds:[esi+0x210],0x9
        jmp m_dwCompareRetAddr2
    b114:
        push m_apcExtendVersion[2]
        push 0x00419FE4
        call m_dwCompareCallAddr
        add esp,0x8
        test eax,eax
        jnz c114
        mov dword ptr ds:[esi+0x210],0xA
        jmp m_dwCompareRetAddr2
    c114:
        push m_apcExtendVersion[3]
        push 0x00419FE4
        call m_dwCompareCallAddr
        add esp,0x8
        test eax,eax
        jnz d114
        mov dword ptr ds:[esi+0x210],0xB
        jmp m_dwCompareRetAddr2
    d114:
        push m_apcExtendVersion[4]
        push 0x00419FE4
        call m_dwCompareCallAddr
        add esp,0x8
        test eax,eax
        jnz out2
        mov dword ptr ds:[esi+0x210],0xC
    out2:
        jmp m_dwCompareRetAddr2
    out1:
        jmp m_dwCompareRetAddr1
    }
}

const DWORD m_dwCompareRetAddr3 = 0x00410308;

__declspec(naked) void D2SE_IsCoreSupport()
{
    __asm
    {
        cmp edx,0x7
        jg d113
        mov edx,dword ptr ds:[edx*4+0x417034]
        jmp m_dwCompareRetAddr3
    d113:
        cmp edx,0x8
        jnz a114
        mov edx,m_apcExtendVersion[0]
        jmp m_dwCompareRetAddr3
    a114:
        cmp edx,0x9
        jnz b114
        mov edx,m_apcExtendVersion[1]
        jmp m_dwCompareRetAddr3
    b114:
        cmp edx,0xA
        jnz c114
        mov edx,m_apcExtendVersion[2]
        jmp m_dwCompareRetAddr3
    c114:
        cmp edx,0xB
        jnz d114
        mov edx,m_apcExtendVersion[3]
        jmp m_dwCompareRetAddr3
    d114:
        cmp edx,0xC
        jnz out1
        mov edx,m_apcExtendVersion[4]
        jmp m_dwCompareRetAddr3
    out1:
        mov edx,dword ptr ds:[0x417054]
        jmp m_dwCompareRetAddr3
    }
}

const DWORD m_dwCompareRetAddr4 = 0x00410ABD;

__declspec(naked) void D2SE_IsCoreInstall()
{
    __asm
    {
        mov eax,dword ptr ds:[eax+0x210]
        cmp eax,0x8
        jl out1
        cmp eax,0xD
        jl out2
        mov eax,0x8
        jmp m_dwCompareRetAddr4
    out2:
        mov eax,0x7
    out1:
        jmp m_dwCompareRetAddr4
    }
}

const DWORD m_dwCompareRetAddr5 = 0x00401A5C;

__declspec(naked) void D2SE_HookD2Dll()
{
    /*
    #因为D2SE会hook一堆dll的函数
    #还需要获取一堆patch地址，参照1.13c，包括以下这些：
    00401000  /$  55            push ebp
    00410AB7    - E9 97961400   jmp D2SE启动.0055A153
    00401A5E  |.  FF15 0C924100 call dword ptr ds:[<&KERNEL32.GetPr>; \GetProcAddress
      1.13c     1.13d   EAX         ECX
    Storm#252   252     6FBF0000    0
    Storm#266   266                 1
    Storm#272   272                 2
    Fog#10019   10019   6FF50000    0
    Fog#10021   10021               1
    Fog#10029   10029               2
    Fog#10042   10042               3
    Fog#10043   10043               4
    Fog#10089   10089               5
    Fog#10090   10090               6
    Fog#10101   10101               7
    Fog#10143   10143               8
    Fog#10218   10218               9
    Fog#10227   10227               A
    D2gfx#0     0       6FA80000    0
    D2gfx#0     0       6FA80000    1
    D2gfx#10034 10071               2
    D2gfx#0     0       6FA80000    3
    D2gfx#10007 10065               4
    D2gfx#0     0       6FA80000    5
    D2gfx#10084 10050               6
    D2gfx#0     0       6FA80000    7
    D2gfx#10053 10008               8
    D2gfx#0     0       6FA80000    9
    D2gfx#10081 10068               A
    D2gfx#0     0       6FA80000    B
    D2gfx#0     0       6FA80000    C
    D2Win#0     0       6F8E0000    0
    D2Win#10086 10174               1
    D2Win#10158 10058               2
    D2Win#10139 10152               3
    D2Win#10005 10072               4
    D2Win#10142 10071               5
    D2Win#10052 10129               6
    D2Win#10073 10140               7
    D2Win#0     0       6F8E0000    8
    D2Win#0     0       6F8E0000    9
    D2Win#10032 10132               A
    D2sound#10002   10023   6F9B0000    0  
    D2sound#10031   10024               1
    D2sound#0   0           6F9B0000    2
    */
    __asm
    {
        add esi,edx
        cmp ebx,0x7
        jg Storm
        mov ebx,dword ptr ds:[esi+ebx*4]
        jmp m_dwCompareRetAddr5
    Storm:
        cmp eax,0x6FBF0000
        jnz Fog
        mov ebx,dword ptr ds:[esi+0x1C]
        jmp m_dwCompareRetAddr5
    Fog:
        cmp eax,0x6FF50000
        jnz D2gfx
        mov ebx,dword ptr ds:[esi+0x1C]
        jmp m_dwCompareRetAddr5
    D2gfx:
        cmp eax,0x6FA80000
        jnz D2Win
        cmp ecx,0x2
        je j2757
        cmp ecx,0x4
        je j2751
        cmp ecx,0x6
        je j2742
        cmp ecx,0x8
        je j2718
        cmp ecx,0xA
        je j2754
        mov ebx,dword ptr ds:[esi+0x1C]
        jmp m_dwCompareRetAddr5
    j2757:
        mov ebx,0x2757
        jmp m_dwCompareRetAddr5
    j2751:
        mov ebx,0x2751
        jmp m_dwCompareRetAddr5
    j2742:
        mov ebx,0x2742
        jmp m_dwCompareRetAddr5
    j2718:
        mov ebx,0x2718
        jmp m_dwCompareRetAddr5
    j2754:
        mov ebx,0x2754
        jmp m_dwCompareRetAddr5
    D2Win:
        cmp eax,0x6F8E0000
        jnz D2sound
        cmp ecx,0x1
        je j27be
        cmp ecx,0x2
        je j274a
        cmp ecx,0x3
        je j27ab
        cmp ecx,0x4
        je j2758
        cmp ecx,0x5
        je jw2757
        cmp ecx,0x6
        je j2791
        cmp ecx,0x7
        je j279c
        cmp ecx,0xA
        je j2794
        mov ebx,dword ptr ds:[esi+0x1C]
        jmp m_dwCompareRetAddr5
    j27be:
        mov ebx,0x27BE
        jmp m_dwCompareRetAddr5
    j274a:
        mov ebx,0x274A
        jmp m_dwCompareRetAddr5
    j27ab:
        mov ebx,0x27A8
        jmp m_dwCompareRetAddr5
    j2758:
        mov ebx,0x2758
        jmp m_dwCompareRetAddr5
    jw2757:
        mov ebx,0x2757
        jmp m_dwCompareRetAddr5
    j2791:
        mov ebx,0x2791
        jmp m_dwCompareRetAddr5
    j279c:
        mov ebx,0x279C
        jmp m_dwCompareRetAddr5
    j2794:
        mov ebx,0x2794
        jmp m_dwCompareRetAddr5
    D2sound:
        cmp eax,0x6F9B0000
        je sound
        mov ebx,dword ptr ds:[esi+0x1C]
        jmp m_dwCompareRetAddr5
    sound:
        cmp ecx,0x0
        je j2727
        cmp ecx,0x1
        je j2728
        mov ebx,dword ptr ds:[esi+0x1C]
        jmp m_dwCompareRetAddr5
    j2727:
        mov ebx,0x2727
        jmp m_dwCompareRetAddr5
    j2728:
        mov ebx,0x2728
        jmp m_dwCompareRetAddr5
    }
}

const DWORD m_dwCompareRetAddr6 = 0x0040D462;
const DWORD m_dwCompareRetAddr7 = 0x0040D529;

__declspec(naked) void D2SE_EnableTxt()
{
    __asm
    {
        cmp dword ptr ds:[eax+0x210],0x7
        je out1
        cmp dword ptr ds:[eax+0x210],0x8
        je out1
        jmp m_dwCompareRetAddr7
    out1:
        jmp m_dwCompareRetAddr6
    }
}

BOOL D2SE_ApplyPatch(void* hGame, const DWORD dwBaseAddress, const DLLPatchStrc* pstPatch)
{
    while ( PATCH_FINISH != pstPatch->dwAddress )
    {
        int iReturn = 0;
        DWORD dwAddress = pstPatch->dwAddress;
        if  ( !dwAddress )
        {
            return FALSE;
        }

        dwAddress += dwBaseAddress;

        DWORD dwData = pstPatch->dwData;
        if ( pstPatch->boolRelative )
        {
            dwData = dwData - (dwAddress + sizeof(dwData));
        }

        void* hAddress = (void*)dwAddress;
        DWORD dwOldPage;

        if ( pstPatch->iPatchSize > 0 )
        {
            BYTE abBuffer[1024];

            for ( size_t i = 0; i < pstPatch->iPatchSize; i++ )
            {
                abBuffer[i] = (BYTE)dwData;
            }

            VirtualProtect(hAddress, pstPatch->iPatchSize, PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &abBuffer, pstPatch->iPatchSize, 0);
            VirtualProtect(hAddress, pstPatch->iPatchSize, dwOldPage, 0);
        }
        else
        {
            VirtualProtect(hAddress, sizeof(dwData), PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &dwData, sizeof(dwData), 0);
            VirtualProtect(hAddress, sizeof(dwData), dwOldPage, 0);
        }

        if ( 0 == iReturn )
        {
            return FALSE;
        }

        pstPatch++;
    }

    return TRUE;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            D2SE_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle(NULL), m_astPatches);
            break;

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

